import numpy as np
from sklearn.datasets import load_iris
from sklearn.datasets import load_diabetes
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.metrics import r2_score
from sklearn.preprocessing import minmax_scale
import matplotlib.pyplot as plt

class BP():
    def __init__(self):
        self.d = 0 #特征数量
        self.q = 0 #隐层结点数量
        self.l = 0 #输出结点数量
        self.W = None
        self.theta = None
        self.V = None
        self.gamma = None
        self.n = 0.1 #学习率
        self.r = 1000 #训练轮数
        return
    
    def structure(self,d,q,l):
        self.d = d
        self.q = q
        self.l = l
        self.W = np.random.rand(q,l)
        self.theta = np.random.rand(1,l)
        self.V = np.random.rand(d,q)
        self.gamma = np.random.rand(1,q)
        return
    
    def Sigmoid(x):
        return 1/(1+np.exp(-x))

    def iterate(self,x,y):
        alpha = np.matmul(x,self.V)
        b = BP.Sigmoid(alpha-self.gamma)
        beta = np.matmul(b,self.W)
        y_p = BP.Sigmoid(beta-self.theta)
        g = np.multiply(np.multiply(y_p,1-y_p),y-y_p)
        delta_W = self.n*np.matmul(b.T,g)
        delta_theta = -self.n*g
        e = np.multiply(np.multiply(b,1-b),np.matmul(g,self.W.T))
        delta_V = self.n*np.matmul(x.T,e)
        delta_gamma = -self.n*e
        self.W += delta_W
        self.theta += delta_theta
        self.V += delta_V
        self.gamma += delta_gamma
        return
    
    def train(self,X,Y):
        X = np.mat(X)
        Y = np.mat(Y)
        m = X.shape[0]
        for r in range(0,self.r):
            for k in range(0,m):
                self.iterate(X[k,:],Y[k,:])
        return
        
    def predict(self,X):
        X = np.mat(X)
        m = X.shape[0]
        Y_p = np.empty([m,self.l])
        for k in range(0,m):
            alpha = np.matmul(X[k,:],self.V)
            b = BP.Sigmoid(alpha-self.gamma)
            beta = np.matmul(b,self.W)
            Y_p[k,:] = BP.Sigmoid(beta-self.theta)
        return Y_p
    
#西瓜数据集
WM=np.loadtxt('./BP/watermelon.txt',skiprows=1,delimiter=' ')
bp4 = BP()
bp4.structure(2,2,1)
bp4.r = 4000
bp4.n = 1
y = np.mat(WM[:,2]).T
bp4.train(WM[:,0:2],y)
y_p = bp4.predict(WM[:,0:2])
x=np.linspace(0,1,40)
y=np.linspace(0,0.5,40)
X,Y=np.meshgrid(x,y)
X = np.reshape(X,[1600,1])
Y = np.reshape(Y,[1600,1])
y_p = bp4.predict(np.append(X,Y,axis=1))
plt.title('watermelon_3a')
plt.xlabel('density')
plt.ylabel('ratio sugar')
plt.scatter(X[y_p>=0.5],Y[y_p>=0.5],marker ='.', color = 'y', s = 10,label= 'predict as good')
plt.scatter(X[y_p<0.5],Y[y_p<0.5],marker ='.', color = 'c', s = 10,label= 'predict as bad')
plt.scatter(WM[WM[:,2]==1,0],WM[WM[:,2]==1,1], marker ='+', color = 'r', s = 100,label= 'good')
plt.scatter(WM[WM[:,2]==0,0],WM[WM[:,2]==0,1], marker ='_',color = 'b',s = 100,label = 'bad')
plt.legend(loc= 'upper right')
plt.show()

#鸢尾花数据集
'''
bp1 = BP()
bp1.structure(4,4,3)
iris = load_iris()
X_train,X_test,y_train,y_test = train_test_split(iris.data,iris.target,stratify=iris.target)
y_train_ = np.append(np.append(np.mat(y_train==0).T,np.mat(y_train==1).T,axis=1),np.mat(y_train==2).T,axis=1)
y_test_ = np.append(np.append(np.mat(y_test==0).T,np.mat(y_test==1).T,axis=1),np.mat(y_test==2).T,axis=1)
bp1.train(X_train,y_train_)
y_p=np.argmax(bp1.predict(X_test),axis=1)
print(classification_report(y_test,y_p))
'''

#糖尿病数据集
'''
diabetes = load_diabetes()
X = minmax_scale(diabetes.data)
y = minmax_scale(diabetes.target)
X_train,X_test,y_train,y_test = train_test_split(X,y)
bp=BP()
bp.structure(10,10,1)
bp.r = 100
bp.train(X_train,np.reshape(y_train,[-1,1]))
print("r2 in train set: ",r2_score(y_train,bp.predict(X_train)))
print("r2 in test set: ",r2_score(y_test,bp.predict(X_test)))
'''
